#include <sstream>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#define CLOSESOCKET(x) closesocket(x)
#define SHUT_RDWR SD_BOTH
#define gmtime_r(a, b) gmtime_s(b, a)
#else
#include <sys/socket.h>
#define CLOSESOCKET(x) ::close(x)
#endif
#include "BBS2chProxyRawSocket.h"

#define HTTP_TIMESTAMP_FMT_PRINT "%a, %d %b %Y %H:%M:%S GMT"

BBS2chProxyRawSocket::BBS2chProxyRawSocket(int sock) :
socket(sock)
{
}

BBS2chProxyRawSocket::~BBS2chProxyRawSocket()
{
}

int BBS2chProxyRawSocket::read(char *buffer, int length)
{
	return recv(socket, buffer, length, 0);
}

int BBS2chProxyRawSocket::readLine(char *buffer, int maxLength)
{
	char *ptr = buffer;
	while (ptr < buffer + maxLength - 1) {
		int read = recv(socket, ptr, 1, 0);
		if (read != 1) {
			return 0;
		}
		if (*ptr++ == '\n') {
			break;
		}
	}
	*ptr = 0;
	return 1;
}

int BBS2chProxyRawSocket::write(const char *buffer, int length)
{
	return send(socket, buffer, length, 0);
}

int BBS2chProxyRawSocket::writeString(const std::string &str)
{
	return send(socket, str.data(), str.length(), 0);
}

void BBS2chProxyRawSocket::close(void)
{
	if (socket >= 0) {
		shutdown(socket, SHUT_RDWR);
#ifdef _WIN32
		Sleep(10);
#else
		usleep(10000);
#endif
		CLOSESOCKET(socket);
		socket = -1;
	}
}

void IBBS2chProxySocket::sendBasicHeaders(int respCode, const char *respMsg)
{
	char date[256];
	time_t now = time(0);
	struct tm tm = {0};
	gmtime_r(&now, &tm);
	strftime(date, 256, HTTP_TIMESTAMP_FMT_PRINT, &tm);
	std::ostringstream ss;
	ss << "HTTP/1.1 " << respCode << " " << respMsg << "\r\n";
	if (0 >= this->writeString(ss.str())) return;
	if (0 >= this->writeString("Connection: Close\r\n")) return;
	if (0 >= this->writeString("Server: proxy2ch\r\n")) return;
	if (0 >= this->writeString(std::string("Date: ") + date + "\r\n")) return;
}

void IBBS2chProxySocket::sendResponse(int respCode, const char *respMsg)
{
	sendBasicHeaders(respCode, respMsg);
	if (0 >= this->writeString("Content-Type: text/plain; charset=UTF-8\r\n")) return;
	if (0 >= this->writeString("\r\n")) return;
	if (respCode >= 400) {
		if (0 >= this->writeString("　　  ∧＿∧　　 ／￣￣￣￣￣\n")) return;
		if (0 >= this->writeString(std::string("　　（ ´∀｀）＜　") + respMsg + "\n")) return;
		if (0 >= this->writeString("　　（　　 　）  ＼＿＿＿＿＿\n")) return;
		if (0 >= this->writeString("　　 │ │  │\n")) return;
		if (0 >= this->writeString("　　（＿_）＿）\n")) return;
	}
}

size_t IBBS2chProxySocket::readChunkedBodyIntoBuffer(char **bufferOut)
{
	size_t contentLength = 0;
	size_t bufferLength = 128;
	size_t chunkSize = 0;
	char tmp[64];
	char *buffer = (char *)malloc(bufferLength);
	while (1) {
		if (!chunkSize) {
			if (!this->readLine(tmp, 64)) break;
			chunkSize = strtol(tmp, NULL, 16);
			if (chunkSize == 0) {
				break;
			}
			if (chunkSize + contentLength >= bufferLength) {
				bufferLength = chunkSize + contentLength + 1;
				buffer = (char *)realloc(buffer, bufferLength);
			}
		}
		size_t ret = this->read(buffer+contentLength, chunkSize);
		if (ret > 0) contentLength += ret;
		if (ret < chunkSize) break;
		if (!this->readLine(tmp, 64)) break;
		chunkSize = 0;
	}
	buffer[contentLength] = 0;
	if (bufferOut) *bufferOut = buffer;
	else free(buffer);
	return contentLength;
}
